/* eslint-disable indent */
/* eslint-disable @typescript-eslint/indent */
/* eslint-disable no-underscore-dangle */
import React, { useReducer, useRef, useState, useEffect, useCallback } from 'react';
import { Radio, Slider, Button, message, Spin, Tooltip } from 'antd';
import { Tree } from 'react-d3-tree';
import { history } from 'umi';
import cn from 'classnames';
import { hasWorkFlowByType } from '@/pages/WorkFlow/utils';
import useOrgTree from '@/pages/Organization/hooks/useOrgTree';
// import { isElementInViewport } from '@/pages/Organization/OrgFlow/OrgTree/utils';
import { cloneDeep } from 'lodash';
import { eventTrack } from '@/utils/track';
import JobRankModal from '@/pages/Organization/OrgFlow/OrgTree/components/JobRankModal';
import JobRankDuty from '@/pages/Organization/OrgFlow/OrgTree/components/JobDutyModal';
import EmployeeTooltip from '@/pages/Organization/OrgFlow/OrgTree/components/EmployeeTooltip';
import OrganizationModal from '@/pages/Organization/OrgFlow/OrgTree/components/OrganizationModal';
import { OrganizationContext } from '../store/context';
import { reducer } from '../store/reducer';
import { batchSaveOrganizationChange } from '../../services';
import useEmployeeListItemCard from '../hooks/useEmployeeListItemCard';
import useEmployeeListItemDuty from '../hooks/useEmployeeListItemDuty';
import useEmployeeListItemRank from '../hooks/useEmployeeListItemRank';
import Node from './node';

import {
    MIN_SCALE,
    MAX_SCALE,
    ORIENTATION,
    INITIAL_ZOOM,
    VERTICAL_CONFIG,
    HORIZONTAL_CONFIG,
    NODE_SIZE,
    DEFAULT_DISTANCE_TOP,
    DEFAULT_DISTANCE_LEFT
} from './constant';

import styles from './org.less';

const OrgTree = ({
    orgTreeId,
    control,
    initData,
    attachData,
    showViewModeSwitch = false,
    onlyTree = false,
    focusCurrentUser = true
}) => {
    const [state, dispatch] = useReducer(reducer, { disableMoveAndZoom: true, submitData: [] });
    const [translate, setTranslate] = useState({ x: 0, y: 0 });
    const [chartConfig, setChartConfig] = useState(VERTICAL_CONFIG);
    const [zoom, setZoom] = useState(INITIAL_ZOOM);
    const chartWrapper = useRef();
    const orgChart = useRef();
    const deletedNode = useRef([]);
    const [focusNodeSwitchStatus, setFocusNodeSwitchStatus] = useState(true);
    const [
        loading,
        treeData,
        submitData,
        orientation,
        toggleOrientation,
        array2Tree,
        addNode,
        removeNode,
        collapseChildrenNode,
        collapseEmployeesNode,
        collapseLeftNode,
        collapseRightNode,
        focusNodeOfTheCurrentUser,
        focusNodeOfAny,
        orgColorMap // 公司及颜色的隐射
    ] = useOrgTree({ initData, attachData });
    const organizationModalRef = useRef(null);
    const employeeTooltipRef = useRef(null);
    useEmployeeListItemCard(orgTreeId, employeeTooltipRef);
    const jobDutyModalRef = useRef(null);
    const jobRankModalRef = useRef(null);
    useEmployeeListItemDuty(orgTreeId, jobDutyModalRef);
    useEmployeeListItemRank(orgTreeId, jobRankModalRef);

    useEffect(() => {
        dispatch({ type: 'UPDATE_SUBMIT_DATA', payload: submitData });
    }, [submitData]);

    // 初始化时设置图表居中
    useEffect(() => {
        setTranslate({ x: chartWrapper.current?.getBoundingClientRect().width / 2, y: DEFAULT_DISTANCE_TOP });
    }, []);

    const handlerOrientationChanged = e => {
        if (e.target.value === ORIENTATION.VERTICAL) {
            toggleOrientation(ORIENTATION.VERTICAL);
            setTranslate({ x: chartWrapper.current?.getBoundingClientRect().width / 2, y: DEFAULT_DISTANCE_TOP });
            setChartConfig(VERTICAL_CONFIG);
        } else {
            toggleOrientation(ORIENTATION.HORIZONTAL);
            setTranslate({ x: DEFAULT_DISTANCE_LEFT, y: chartWrapper.current?.getBoundingClientRect().height / 2 });
            setChartConfig(HORIZONTAL_CONFIG);
        }
        setZoom(INITIAL_ZOOM);
    };

    const handlerZoomChanged = value => {
        setZoom(value);
    };

    const handlerZoomIn = () => {
        setZoom(z => (z + 0.1 < MAX_SCALE ? z + 0.1 : MAX_SCALE));
    };

    const handlerZoomOut = () => {
        setZoom(z => (z - 0.1 > MIN_SCALE ? z - 0.1 : MIN_SCALE));
    };

    const handlerResetPoint = () => {
        if (orientation === ORIENTATION.VERTICAL) {
            setTranslate({ x: chartWrapper.current?.getBoundingClientRect().width / 2, y: DEFAULT_DISTANCE_TOP });
        } else {
            setTranslate({ x: DEFAULT_DISTANCE_LEFT, y: chartWrapper.current?.getBoundingClientRect().height / 2 });
        }
        // 只有变更 zoom 才会触发 react-d3-tree 内部的状态
        setZoom(z => z - 0.00001);
        setTimeout(() => {
            setZoom(z => z + 0.00001);
        });
    };

    const updateLine = () => {
        const nodes = new Set(deletedNode.current);
        nodes.forEach(node => {
            const line = document.querySelector(node);
            if (line) {
                line.setAttribute('stroke-dasharray', '5, 5');
            }
        });

        deletedNode.current = [];
    };

    const handlerTreeUpdate = ({ zoom: currentZoom, translate: translateUpdate }) => {
        if (currentZoom !== zoom) {
            setZoom(currentZoom);
            setTranslate(translateUpdate);
        }

        updateLine();
    };

    // 提交审批
    const [submitLoading, setSubmitLoading] = useState(false);
    const handleSubmit = async () => {
        if (submitLoading) {
          return;
        }
        setSubmitLoading(true);
        await eventTrack({
            eventName: 'personnel_management_organization_orgflow_graph_view_context_click',
            pageName: 'personnel_management_organization',
            eventProps: {
                click_context: '提交审批'
            }
        });
        // 流程不存在提示
        const { workFlowUrl } = await hasWorkFlowByType('organizationApply');
        if (!workFlowUrl) {
            return;
        }

        // 预处理数据
        const preprocessData = item => {
            const newData = cloneDeep(item.data);
            if (newData?.area?.length) {
                newData.area = newData.area.join(',');
            }
            delete newData.leftNode;
            delete newData.rightNode;
            delete newData.hidden;
            delete newData._collapsed;
            return newData;
        };

        // 新增的数据
        const addData = array2Tree(submitData.filter(item => item.type === 'add').map(preprocessData));
        // 删除的数据
        const deleteData = submitData.filter(item => item.type === 'delete').map(preprocessData);
        // 重新排序的的排序
        const sortData = {};
        // 取得所有添加节点的兄弟的排序序列
        addData.forEach(item => {
            let queue = [treeData];
            while (queue.length) {
                const currentItem = queue?.pop();
                if (currentItem && currentItem.data.organizationPath === item.parentOrganizationPath) {
                    sortData[item.parentOrganizationPath] = currentItem.children.map(
                        child => child.data.organizationPath
                    );
                    queue = [];
                } else if (currentItem?.children) {
                    queue = queue?.concat(currentItem.children);
                }
            }
        });
        // 从序列中删除掉被删除的数据
        deleteData.forEach(item => {
            if (sortData[item.parentOrganizationPath]?.length) {
                sortData[item.parentOrganizationPath] = sortData[item.parentOrganizationPath].filter(
                    sortItem => sortItem !== item.organizationPath
                );
            }
        });
        // 分组放置要提交的数据
        const groupSubmitData = {
            add: addData,
            delete: deleteData,
            sort: sortData // 得到的数据是 父节点id: [子节点id, 子节点id, 子节点id]
        };
        // 提交更新
        batchSaveOrganizationChange(groupSubmitData)
            .then(async res => {
                const { msg: sMsg, data: id, code: sCode } = res;
                if (sCode === 10000) {
                    history.push(`${workFlowUrl}${id}`);
                } else {
                    message.error(sMsg);
                }
            })
            .catch(console.error)
            .finally(() => {
                setSubmitLoading(false);
            });
    };

    const customPath = (linkData, direction) => {
        const { source, target } = linkData;
        const deltaY = target.y - source.y;

        if (target.data.deleted) {
            deletedNode.current.push(`[data-target-id="${target.id}"]`);
        }

        return direction === 'horizontal'
            ? `M${source.y},${source.x} H${source.y + deltaY / 2} V${target.x} H${
                  target._children ? target.y + 168 : target.y
              }`
            : `M${source.x},${target.depth === 1 ? source.y - 30 : source.y} V${source.y + deltaY / 3} H${target.x} V${
                  target._children ? target.y : target.y - 38
              }`;
    };

    // 让当前操作的节点居中
    const centerTheNode = (node, callback, isRestPosition = false) => {
        const rootSvg = document.querySelector(`#${orgTreeId} .rd3t-tree-container > svg > g`);
        rootSvg.style.visibility = 'hidden';
        // 记录旧值的节点距离中线的距离
        const oldCurrentNodeDom = document.querySelector(`div[data-org-path="${node.data.organizationPath}"]`);
        const oldOffset = Number(oldCurrentNodeDom.dataset.x);
        // 执行变化
        callback();
        // 使用 setTimeout 是确保获取的节点是重新定位后的节点, 这样才有办法计算位置
        setTimeout(() => {
            rootSvg.style.visibility = 'visible';
            // 取得变化后的节点
            const currentNodeDom = document.querySelector(`div[data-org-path="${node.data.organizationPath}"]`);
            const isVertical = orientation === ORIENTATION.VERTICAL;
            // 高亮当前操作的节点
            // const currentNodeCardDom = currentNodeDom.querySelector('.nodeCard');
            // const cacheClassName = currentNodeCardDom.className;
            // currentNodeCardDom.className = `${cacheClassName} highlight`;
            // setTimeout(() => {
            //     currentNodeCardDom.className = cacheClassName;
            // }, 300);
            // 如果当前节点在视图之外, 就居中显示当前操作的节点
            const currentOffset = Number(currentNodeDom.dataset.x);
            if (currentNodeDom && rootSvg) {
                // !isElementInViewport(currentNodeDom, 127)
                if (isVertical) {
                    let xVal = Number(currentNodeDom.dataset.x);
                    const yVal = rootSvg.transform?.baseVal[0]?.matrix?.f;

                    if (isRestPosition) {
                        xVal = chartWrapper.current?.getBoundingClientRect().width / 2 - xVal * zoom;
                    } else {
                        // 计算节点前后变化的坐标差值
                        const rootX = rootSvg.transform?.baseVal[0]?.matrix?.e;
                        const diffVal = (currentOffset - oldOffset) * zoom;
                        if (currentOffset > oldOffset) {
                            xVal = rootX - (diffVal > 0 ? diffVal : -diffVal);
                        } else {
                            xVal = rootX + (diffVal > 0 ? diffVal : -diffVal);
                        }
                    }

                    setTranslate({
                        x: xVal,
                        y: yVal || DEFAULT_DISTANCE_TOP
                    });
                } else {
                    const xVal = rootSvg.transform?.baseVal[0]?.matrix?.e;
                    let yVal = Number(currentNodeDom.dataset.x);

                    if (isRestPosition) {
                        yVal = chartWrapper.current?.getBoundingClientRect().height / 2 - yVal * zoom - 200;
                    } else {
                        // 计算节点前后变化的坐标差值
                        const rootY = rootSvg.transform?.baseVal[0]?.matrix?.f;
                        const diffVal = (currentOffset - oldOffset) * zoom;
                        if (currentOffset > oldOffset) {
                            yVal = rootY - (diffVal > 0 ? diffVal : -diffVal);
                        } else {
                            yVal = rootY + (diffVal > 0 ? diffVal : -diffVal);
                        }
                    }

                    setTranslate({
                        x: xVal || DEFAULT_DISTANCE_LEFT,
                        y: yVal
                    });
                }
            }
        }, 16);
    };

    const handleAddNode = useCallback(
        (node, data) => {
            addNode(data);
        },
        [addNode]
    );

    const handleRemoveNode = useCallback(
        (node, data) => {
            removeNode(data);
        },
        [removeNode]
    );

    const handleCollapseEmployeesNode = useCallback(
        (node, visible) => {
            collapseEmployeesNode(node, visible);
        },
        [collapseEmployeesNode]
    );

    const handleCollapseLeftNode = (node, hiddenStatus) => {
        centerTheNode(node, () => {
            collapseLeftNode(node.data, hiddenStatus);
        });
    };

    const handleCollapseRightNode = (node, hiddenStatus) => {
        centerTheNode(node, () => {
            collapseRightNode(node.data, hiddenStatus);
        });
    };

    const handleCollapseChildrenNode = node => {
        centerTheNode(node, () => {
            collapseChildrenNode(node);
        }, true);
    };

    const handleFocusNode = node => {
        centerTheNode(node, () => {
            focusNodeOfAny([node]);
        }, true);
    };

    const handlerFocusCurrentUserNode = () => {
        focusNodeOfTheCurrentUser(focusNodeSwitchStatus);
        setFocusNodeSwitchStatus(!focusNodeSwitchStatus);
        if (orientation === ORIENTATION.VERTICAL) {
            setTranslate({ x: chartWrapper.current?.getBoundingClientRect().width / 2, y: DEFAULT_DISTANCE_TOP });
        } else {
            setTranslate({
                x: DEFAULT_DISTANCE_LEFT,
                y: chartWrapper.current?.getBoundingClientRect().height / 2 - DEFAULT_DISTANCE_TOP
            });
        }
        // 埋点
        eventTrack({
            eventName: 'personnel_management_organization_orgflow_graph_view_context_click',
            pageName: 'personnel_management_organization',
            eventProps: { click_context: focusNodeSwitchStatus ? '展开节点' : '收缩节点' }
        });
    };

    // 查看岗位职责
    const handleClickJobDuty = dutyCode => jobDutyModalRef.current?.open(dutyCode);

    // 查看职级描述
    const handleClickJobRank = rankCode => jobRankModalRef.current?.open(rankCode);

    // 查看组织信息
    const handleClickOrganizationModal = organizationCode => organizationModalRef.current?.open(organizationCode);

    const tipFormatter = value => `${Math.round(value * 100)}%`;

    return (
        <div
            className={cn(styles.orgChartWrapper, `org-tree-orientation-${orientation}`, 'degradedOrgTree')}
            ref={chartWrapper}
            style={{ overflow: control ? 'hidden' : 'visible' }}
        >
            {loading && (
                <div className={styles.loadingWrapper}>
                    <Spin />
                </div>
            )}
            {/* 图例 */}
            <div className={cn(styles.legend, control && styles.legendOffset)}>
                {orgColorMap &&
                    Object.values(orgColorMap).map(item => (
                        <div className={styles.legendItem} key={item.organizationCode}>
                            <i className={styles.legendItemColor} style={{ backgroundColor: item.colorCode }} />
                            {item.organizationName}
                        </div>
                    ))}
            </div>
            <div id="orgChartOrientation" className={styles.orgChartOrientation}>
                {focusCurrentUser && (
                    <Tooltip placement="bottom" title={focusNodeSwitchStatus ? '展开节点' : '收缩节点'}>
                        <Button onClick={handlerFocusCurrentUserNode}>
                            {focusNodeSwitchStatus ? (
                                <i
                                    className="iconfont iconunfold_joint_icon_default"
                                    style={{ fontSize: '20px', transform: 'translateY(2px)' }}
                                />
                            ) : (
                                <i
                                    className="iconfont iconfold_joint_icon_default"
                                    style={{ fontSize: '20px', transform: 'translateY(2px)' }}
                                />
                            )}
                        </Button>
                    </Tooltip>
                )}
                {showViewModeSwitch && (
                    <Radio.Group
                        defaultValue={ORIENTATION.VERTICAL}
                        buttonStyle="outline"
                        onChange={handlerOrientationChanged}
                        style={{ marginLeft: '5px' }}
                    >
                        <Tooltip placement="bottom" title="切换横向视图">
                            <Radio.Button value={ORIENTATION.VERTICAL}>
                                <span
                                    className={cn(styles.treeVertical, 'icon iconfont iconview_horizonal_icon_default')}
                                />
                            </Radio.Button>
                        </Tooltip>
                        <Tooltip placement="bottom" title="切换纵向视图">
                            <Radio.Button value={ORIENTATION.HORIZONTAL}>
                                <span
                                    className={cn(
                                        styles.treeHorizontal,
                                        'icon iconfont iconview_vertical_icon_default'
                                    )}
                                />
                            </Radio.Button>
                        </Tooltip>
                    </Radio.Group>
                )}
                {control && !attachData && (
                    <div className={styles.submitButton}>
                        <Button
                            disabled={!submitData || submitData.length === 0 || submitLoading}
                            type="primary"
                            onClick={handleSubmit}
                        >
                            提交审批
                        </Button>
                    </div>
                )}
            </div>
            {treeData && (
                <div id="orgChartControl" className={styles.orgChartControl}>
                    <div className={styles.sliderControl}>
                        <i
                            className={cn(styles.sliderControlZoomIn, 'iconfont iconzoomin_o')}
                            onClick={handlerZoomIn}
                        />
                        <Slider
                            vertical
                            defaultValue={INITIAL_ZOOM}
                            value={zoom}
                            step={0.1}
                            min={MIN_SCALE}
                            max={MAX_SCALE}
                            onChange={handlerZoomChanged}
                            tipFormatter={tipFormatter}
                        />
                        <i
                            className={cn(styles.sliderControlZoomOut, 'iconfont iconzoomout_o')}
                            onClick={handlerZoomOut}
                        />
                        <i
                            className={cn(styles.sliderControlResetPoint, 'iconfont iconlocate_o')}
                            onClick={handlerResetPoint}
                        />
                        {/* <Icon type="printer" className={styles.sliderControlPrint} onClick={handlerPrint} /> */}
                    </div>
                </div>
            )}
            <OrganizationContext.Provider value={{ state, dispatch }}>
                {treeData && (
                    <div id={orgTreeId} className={styles.orgTreeContainer}>
                        <Tree
                            ref={orgChart}
                            data={treeData}
                            allowForeignObjects
                            transitionDuration={0}
                            collapsible
                            orientation={orientation}
                            shouldCollapseNeighborNodes
                            zoomable={state.disableMoveAndZoom}
                            zoom={zoom}
                            control={control}
                            onUpdate={handlerTreeUpdate}
                            nodeSvgShape={{
                                shape: 'foreignObject',
                                shapeProps: {
                                    x: chartConfig.shapePoint.x,
                                    y: chartConfig.shapePoint.y,
                                    width: '25px',
                                    height: '25px',
                                    // xmlns: 'http://www.w3.org/2000/svg',
                                    // requiredExtensions: 'http://www.w3.org/1999/xhtml',
                                    children: (
                                        <div className="nodeShape">
                                            <span className="shape" />
                                        </div>
                                    )
                                }
                            }}
                            onClick={handleCollapseChildrenNode}
                            pathFunc={customPath}
                            translate={translate}
                            textLayout={{ textAnchor: 'start', x: 10, y: -10, transform: undefined }}
                            nodeSize={{ x: NODE_SIZE.x, y: NODE_SIZE.y }}
                            separation={{ siblings: orientation === ORIENTATION.VERTICAL ? 1.2 : 1.8, nonSiblings: 2 }}
                            depthFactor={chartConfig.depthFactor}
                            scaleExtent={{ min: MIN_SCALE, max: MAX_SCALE }}
                            nodeLabelComponent={{
                                render: (
                                    <Node
                                        treeId={orgTreeId}
                                        orientation={orientation}
                                        control={control}
                                        onlyTree={onlyTree}
                                        onAddCompany={handleAddNode}
                                        onAddDepartment={handleAddNode}
                                        onDelete={handleRemoveNode}
                                        onCollapseEmployeesNode={handleCollapseEmployeesNode}
                                        onCollapseLeftNode={handleCollapseLeftNode}
                                        onCollapseRightNode={handleCollapseRightNode}
                                        orgColorMap={orgColorMap}
                                        onFocusNode={handleFocusNode}
                                        onClickJobRank={handleClickJobRank}
                                        onClickJobDuty={handleClickJobDuty}
                                        onClickOrganizationModal={handleClickOrganizationModal}
                                    />
                                ),
                                foreignObjectWrapper: {
                                    x: chartConfig.foreignObjectPoint.x,
                                    y: chartConfig.foreignObjectPoint.y,
                                    width: orientation === ORIENTATION.VERTICAL ? '300px' : '254px',
                                    height: orientation === ORIENTATION.VERTICAL ? '72px' : '132px'
                                    // xmlns: 'http://www.w3.org/2000/svg',
                                    // requiredExtensions: 'http://www.w3.org/1999/xhtml'
                                }
                            }}
                        />
                        <EmployeeTooltip ref={employeeTooltipRef} />
                        <JobRankModal ref={jobRankModalRef} />
                        <JobRankDuty ref={jobDutyModalRef} />
                        <OrganizationModal ref={organizationModalRef} />
                    </div>
                )}
            </OrganizationContext.Provider>
        </div>
    );
};

export default OrgTree;
